package br.com.visagio.aulabigdata.business;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;

import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrQuery.ORDER;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.hibernate.Query;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import twitter4j.JSONObject;
import twitter4j.Status;
import twitter4j.TwitterException;
import br.com.visagio.aulabigdata.enums.DB;
import br.com.visagio.aulabigdata.models.Tweet;
import br.com.visagio.aulabigdata.util.MongoDBUtil;
import br.com.visagio.aulabigdata.util.MySQLUtil;
import br.com.visagio.aulabigdata.util.ParserUtil;
import br.com.visagio.aulabigdata.util.SolrUtil;
import br.com.visagio.aulabigdata.util.TwitterUtil;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;

public class TwitterBusiness {
	
	Logger logger = LoggerFactory.getLogger(TwitterBusiness.class);
	
	public final static String FIELD_ID = "id";
	public final static String FIELD_TIMESTAMP = "timestamp";
	public final static String FIELD_URL = "profileImageURL";
	public final static String FIELD_ = "timestamp";
	public final static String FIELD_CREATE_DATE = "createdAt";

	private List<DB> usedDatabases;
	
	public void searchAndSaveLatestTweets() throws TwitterException {
		searchAndSaveTweets(true);
	}
	
	public void searchAndSaveAllTweets() throws TwitterException {
		searchAndSaveTweets(false);
	}
	
	private void searchAndSaveTweets(boolean untilMostRecently) throws TwitterException {
		Date latestTweetDate = getLatestTweetDate();
		if (latestTweetDate == null) {
			latestTweetDate = new Date(0l);
		}
		
		int nRetrieved = 0;
		
		while(!TwitterUtil.hasReachedSearchLimit(nRetrieved)){
			for (Status tweet : TwitterUtil.getTweetsByHashtag(TwitterUtil.HASHTAG)) {
				if (untilMostRecently && tweet.getCreatedAt().compareTo(latestTweetDate) < 0) {
					logger.info("Tweet descartado: id=" + tweet.getId() + " | Data criação: " + tweet.getCreatedAt());
					if (nRetrieved == 0)logger.info("Nenhum tweet novo encontrado.");
					continue;
				}
				nRetrieved++;
				Tweet t = saveTweet(tweet);
				if(t != null){
					logger.info("Tweet " + nRetrieved + " salvo : " + t.toString());
				}
			}
		}
		logger.info("Atingiu o limite de requisições do Twitter(" + nRetrieved + ")");
	}
	
	public Tweet saveTweet(Status tweet) {
		Tweet tweetBean = null;
		if(this.usedDatabases.contains(DB.MONGO)){
			saveTweetMongo(tweet,MongoDBUtil.COLLECTION);
		}
		if(this.usedDatabases.contains(DB.SOLR)){
			tweetBean = saveTweetSolr(tweet);
		}
		if(this.usedDatabases.contains(DB.MYSQL)){
			tweetBean = saveTweetMySQL(tweet);
		}
		return tweetBean;
	}
	
	private Tweet saveTweetMySQL(Status tweet) {
		Tweet tweetBean = new Tweet(tweet);
		Session session = MySQLUtil.getInstance().getSession();
		session.beginTransaction();
		session.saveOrUpdate(tweetBean);
		session.getTransaction().commit();
		return tweetBean;
	}

	public Tweet saveTweetSolr(Status tweet) {
		Tweet tweetBean = new Tweet(tweet);
		SolrServer server = SolrUtil.getInstance().getServer();
		try {
			server.addBean(tweetBean);
			server.commit();
			return tweetBean;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SolrServerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public void saveTweetMongo(Status tweet, String collectionName) {
		DBCollection collection = MongoDBUtil.getInstance().getDatabase().getCollection(collectionName);
		DBObject dbObject = (DBObject) JSON.parse(new JSONObject(tweet).toString());
		dbObject.put(FIELD_TIMESTAMP, ParserUtil.parseDate(dbObject.get(FIELD_CREATE_DATE).toString()).getTime());
		BasicDBObject query = new BasicDBObject(FIELD_ID, dbObject.get(FIELD_ID));
		try {
			collection.update(query, dbObject, true, false);
			logger.info("Tweet salvo: {}", tweet);
		} catch (Exception e) {
			logger.error("Erro ao salvar tweet \n" + e.toString());
		}
	}
	
	public Date getLatestTweetDate() {
		if(this.usedDatabases.contains(DB.MONGO)){
			return getLatestTweetDateMongo(MongoDBUtil.COLLECTION);
		}
		if(this.usedDatabases.contains(DB.SOLR)){
			return getLatestTweetDateSolr();
		}
		if(this.usedDatabases.contains(DB.MYSQL)){
			return getLatestTweetDateMySQL();
		}
		return null;
	}
	
	private Date getLatestTweetDateMySQL() {
		Session session = MySQLUtil.getInstance().getSession();
		String hql = "SELECT max(t.createdAt) from " + Tweet.class.getName() + " t ";
		Query query = session.createQuery(hql);
		Date date = (Date) query.uniqueResult();
		return date;
	}

	private Date getLatestTweetDateSolr() {
		SolrServer server = SolrUtil.getInstance().getServer();
		SolrQuery query = new SolrQuery("*:*");
		query.addSort(FIELD_CREATE_DATE, ORDER.desc);
		QueryResponse result = null;
		try {
			result = server.query(query);
		} catch (SolrServerException e) {
			logger.warn("Erro ao obter tweet mais recente " + e.getMessage());
		}
		if(result == null || result.getResults().size() == 0) return null;
		if(result.getResults().get(0) == null) return null;
		return (Date) result.getResults().get(0).get(FIELD_CREATE_DATE);
	}

	public Date getLatestTweetDateMongo(String collectionName) {
		DBCollection collection = MongoDBUtil.getInstance().getDatabase().getCollection(collectionName);
		try {
			DBObject latestTweet = collection.find(new BasicDBObject()).sort(new BasicDBObject(FIELD_TIMESTAMP, -1)).limit(1).next();
			return ParserUtil.parseTimestamp(latestTweet.get(FIELD_TIMESTAMP).toString());
		} catch (NoSuchElementException e) {
			return null;
		}
	}
	
	public void removeAllTweets() {
		if(this.usedDatabases.contains(DB.MONGO)){
			removeAllTweetsMongo(MongoDBUtil.COLLECTION);
		}
		if(this.usedDatabases.contains(DB.SOLR)){
			SolrUtil.getInstance().removeAllTweets();
		}
		if(this.usedDatabases.contains(DB.MYSQL)){
			removeAllTweetsMySQL();
		}
	}


	private void removeAllTweetsMySQL() {
		Session session = MySQLUtil.getInstance().getSession();
		String hql = "DELETE FROM " + Tweet.class.getName();
		Query query = session.createQuery(hql);
		query.executeUpdate();
	}

	public void removeAllTweetsMongo(String collectionName) {
		DBCollection collection = MongoDBUtil.getInstance().getDatabase().getCollection(collectionName);
		collection.remove(new BasicDBObject());
	}

	public static void main(String[] args) throws TwitterException {
		TwitterBusiness twitterBusiness = new TwitterBusiness();
	}

	public List<DB> getDatabaseOption() {
		return usedDatabases;
	}

	public void setDatabaseOption(List<DB> databases) {
		usedDatabases = databases;
	}
	
}
